package de.bht.mi.kriz.multitouch;

/**
 * based on http://www.anddev.org/basic_drag_and_drop-t3095.html
 * 
 * Features: 
 * 
 * 19.12.2011: Single Point Drag & Drop
 * 19.12.2011: Convex Hull Operation to show area relationship between points
 * 21.12.2011: Bring on top by selecting ball
 * 21.12.2011: Adding new Balls by clicking on empty canvas
 * 21.12.2011: Calculation if ball is in convex hull -> reduce alphavalue  
 * 21.12.2011: Changed to use Multitouch Controller Class 
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.ListIterator;
import java.util.Random;

import org.metalev.multitouch.controller.MultiTouchController;
import org.metalev.multitouch.controller.MultiTouchController.MultiTouchObjectCanvas;
import org.metalev.multitouch.controller.MultiTouchController.PointInfo;
import org.metalev.multitouch.controller.MultiTouchController.PositionAndScale;

import de.bht.mi.kriz.multitouch.calc.GrahamScan;
import de.bht.mi.kriz.multitouch.components.ColorBall;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class ElementView extends View  implements MultiTouchObjectCanvas<Object> {
	private static final int TOUCH_RADIUS_TOLLERANCE = 8;
	private ArrayList<ColorBall> ballStack = new ArrayList<ColorBall>();
	
	private ArrayList<Integer> bitmapResources = new ArrayList<Integer>();
	private Random randomGenerator = new Random();
	
	private MultiTouchController<Object> multiTouchController;
	
	private int getRandomBitmap()
	{
		return bitmapResources.get(randomGenerator.nextInt(bitmapResources.size()));
	}
	
	private Point getRandomPoint()
	{
		//generates pseudo random Points between (0,0) and (500,500)
		return new Point(randomGenerator.nextInt(500),randomGenerator.nextInt(500)); 
	}
	
	public ElementView(Context context) {
		super(context);
		
		multiTouchController = new MultiTouchController<Object>(this);
		
		setFocusable(true); // necessary for getting the touch events
		
		bitmapResources.add(R.drawable.bol_groen);
		bitmapResources.add(R.drawable.bol_geel);
		bitmapResources.add(R.drawable.bol_blauw);
		bitmapResources.add(R.drawable.bol_paars);
		bitmapResources.add(R.drawable.bol_rood);
		
		// adding 5 ColorBall Objects to the stack
		ballStack.add(new ColorBall(this, context, getRandomBitmap(), getRandomPoint()));
		ballStack.add(new ColorBall(this, context, getRandomBitmap(), getRandomPoint()));
		ballStack.add(new ColorBall(this, context, getRandomBitmap(), getRandomPoint()));
		ballStack.add(new ColorBall(this, context, getRandomBitmap(), getRandomPoint()));
		ballStack.add(new ColorBall(this, context, getRandomBitmap(), getRandomPoint()));
	}

	// the method that draws the balls
	@Override
	protected void onDraw(Canvas canvas) {
		
		SharedPreferences settings = getContext().getSharedPreferences("OptionMenu", android.content.Context.MODE_PRIVATE);
		
		Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
		
		if(settings.getBoolean("fillArea", false)) 
		{
			p.setStyle(Style.FILL);
		} else {
			p.setStyle(Style.STROKE);
		}
		
		p.setStrokeWidth(3f);
		Path pth = new Path();

		ColorBall[] colorballs = new ColorBall[ballStack.size()];
		de.bht.mi.kriz.multitouch.calc.Point[] colorballPoints = new de.bht.mi.kriz.multitouch.calc.Point[ballStack.size()];
		int i = 0;

		for (ListIterator<ColorBall> ballIterator = ballStack.listIterator(); ballIterator.hasNext(); i++) {
			colorballs[i] = ballIterator.next();
			colorballPoints[i] = new de.bht.mi.kriz.multitouch.calc.Point(colorballs[i].getX(), colorballs[i].getY());
		}

		GrahamScan gh = new GrahamScan();
		int h = gh.computeHull(colorballPoints);

		// draw the convex hull for the balls on canvas
		i = 0;
		for (de.bht.mi.kriz.multitouch.calc.Point pnt : colorballPoints) 
		{
			if (i >= h) {
				break;
			}

			if (pth.isEmpty()) {
				pth.moveTo((float) pnt.x, (float) pnt.y);
				pth.lineTo((float) pnt.x, (float) pnt.y);
			} else {
				pth.lineTo((float) pnt.x, (float) pnt.y);
			}

			i++;
		}

		pth.close();

		p.setColor(0xffcccccc);
		canvas.drawPath(pth, p);
		p.setAlpha(128);
		
		Region innerArea = new Region();
		RectF bounds = new RectF();
		
		pth.computeBounds(bounds, true);
		Region boundRegion = new Region((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom);
		
		innerArea.setPath(pth, boundRegion);

		final float grapTollerance = 3.0f;
		
		// draw the balls on top on canvas
		for (ColorBall ball : colorballs) 
		{
			
			// inside the convex hull -> draw with alpha
			if(innerArea.contains((int)(ball.getX()-grapTollerance), (int)(ball.getY()-grapTollerance)) && innerArea.contains((int)(ball.getX()+grapTollerance), (int)(ball.getY()+grapTollerance))) 
			{
				canvas.drawBitmap(ball.getBitmap(), ball.getDrawX(), ball.getDrawY(), p);
			} else {
				canvas.drawBitmap(ball.getBitmap(), ball.getDrawX(), ball.getDrawY(), null);
			}
			
			
		}

	}

	// events when touching the screen
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// Pass the event on to the controller
		return multiTouchController.onTouchEvent(event);
	}
	
	public Object getDraggableObjectAtPoint(PointInfo pt) {
		// IMPORTANT: to start a multitouch drag operation, this routine must return non-null
		
		//reverse the ballStack order look top down
		ArrayList<ColorBall> tempStack = new ArrayList<ColorBall>(ballStack);
		Collections.reverse(tempStack);
		
		for(int i=0; i<pt.getNumTouchPoints(); i++) {
			for (ColorBall ball : tempStack) {
				// check if inside the bounds of the ball (circle)
				// get the center for the ball
				
				PositionAndScale ps = new PositionAndScale();
				this.getPositionAndScale(ball, ps);
				
				float centerX = ps.getXOff();
				float centerY = ps.getYOff();
				
				Log.d("func_getDraggableObjectAtPoint", "TEST:"+ball.getX()+" = "+ps.getXOff()+" = "+pt.getX());
	
				// calculate the radius from the touch to the center of the ball
				double radCircle = Math.sqrt((((centerX - pt.getX()) * (centerX - pt.getX())) + (centerY - pt.getY()) * (centerY - pt.getY())));
	
				// if the radius is smaller then radius of a ball,
				// then it must be on the ball
				// once detected handle it till release
				if (radCircle < ball.getBitmap().getWidth()/2 + TOUCH_RADIUS_TOLLERANCE) {
	
					Log.d("func_getDraggableObjectAtPoint", "Object found: "+ball.toString());
					return ball;
				}
	
			}
		}
		
		return this;
	}

	public void getPositionAndScale(Object obj, PositionAndScale objPosAndScaleOut) {
		
		if(obj instanceof ColorBall)
		{
			ColorBall ball = (ColorBall)obj;
			objPosAndScaleOut.set(ball.getX(), ball.getY(), false, 0, false, 0, 0, false, 0);
		}
	}

	public void selectObject(Object obj, PointInfo touchPoint) {
		
		SharedPreferences settings = getContext().getSharedPreferences("OptionMenu", android.content.Context.MODE_PRIVATE);
		
		if(obj instanceof ColorBall)
		{
			Log.d("func_selectObject", obj.toString());
			
			//remove and readd to put element on top of stack -> bring to front
			ballStack.remove(obj);
			ballStack.add((ColorBall) obj);
		} else if(obj == null && settings.getBoolean("addBalls", false)) {
			ColorBall newBall = new ColorBall(this, getContext(), getRandomBitmap(), new Point((int)touchPoint.getX(), (int)touchPoint.getY()));
			ballStack.add(newBall);
			invalidate();
		}
	}

	public boolean setPositionAndScale(Object obj, PositionAndScale newObjPosAndScale, PointInfo touchPoint) {
		// Called during a drag or stretch operation, update the touch point info
		
		if(obj instanceof ColorBall) 
		{
			ColorBall ball = (ColorBall)obj;
			Log.d("func_setPositionAndScale", newObjPosAndScale.toString());
			
			SharedPreferences settings = getContext().getSharedPreferences("OptionMenu", android.content.Context.MODE_PRIVATE);
			if(settings.getBoolean("flingMode", false)) {
				// http://mobile.tutsplus.com/tutorials/android/android-gesture/
				float velocityX, velocityY, totalDx, totalDy;
				velocityX = newObjPosAndScale.getXOff() - ball.getX();
				velocityY = newObjPosAndScale.getYOff() - ball.getY();
				
				float distanceTimeFactor = 0.3f;
				totalDx = (velocityX/2);
				totalDy = (velocityY/2);
				ball.onAnimateMove(totalDx, totalDy, (long) (1000 * distanceTimeFactor), settings.getBoolean("bouncyWalls", false));
			} else {
				ball.setX(newObjPosAndScale.getXOff());
				ball.setY(newObjPosAndScale.getYOff());
			}
			
			Log.d("func_setPositionAndScale", ""+ball.getX());
			Log.d("func_setPositionAndScale", "TEST:"+newObjPosAndScale.getXOff()+" = "+touchPoint.getX());
		}
		
		invalidate();
		return true;
	}
}
